package com.qire.antsrouter;

import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManager;
import android.util.Log;

import com.qire.antscore.IInterceptorLoader;
import com.qire.antscore.IRouteGroup;
import com.qire.antscore.IRouteRoot;
import com.qire.antscore.common.AntsThreadBuilder;
import com.qire.antscore.common.AssertUtils;
import com.qire.antscore.constant.GeneratorConfig;
import com.qire.antscore.entity.RouteMeta;
import com.qire.antscore.entity.RulesMeta;
import com.qire.antsrouter.exception.NoRouteFoundException;
import com.qire.antsrouter.intercept.InterceptRules;
import com.qire.antsrouter.utils.ClassUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * 映射缓存（包含路由映射表、拦截器映射表等）
 */
public class MappingCache {

    private static final String TAG = "AntsRouter";

    // routeMeta 按组加载缓存映射表，以组别划分按组实现懒加载缓存表，key：组别，value：生成封装好的
    private static final Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();

    // routeMeta 实体映射表，
    private static final Map<String, RouteMeta> routes = new HashMap<>();

//    // group 映射表 保存组中的所有数据
//    static Map<Class, IService> services = new HashMap<>();
    // TestServiceImpl.class , TestServiceImpl 没有再反射

    // 拦截器索引映射表 保存拦截器规则索引用来构建拦截器组群                        以键值对优先级的方式保存拦截器对象
    private static final Map<Integer, RulesMeta> interceptorsIndex = new TreeMap<>();

    // 拦截器规则映射表 保存所有拦截器规则对象实体，以分组形式保存                   以集合的方式保存所有拦截器对象
    private static final Map<String, List<InterceptRules>> interceptorGroup = new HashMap<>();

    /**
     * 扫描指定包名路径下的类文件，并且找出组群路由表及组群拦截器表，实现懒加载，在具体使用时在按组群加载具体映射表。
     */
    protected static void loadInfo(Application application) throws PackageManager.NameNotFoundException, InterruptedException,
            ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        // 获得所有 apt生成的路由类的全类名 (路由表)
        // 这个部分由于会要解压缩和读取文件，存在IO操作，在某些手机特别是华为手机上会有比较明显的延时,
        // 虽然使用了多线程但由于大部分时候我们加载的DEX只有一份所以对于提速并不明显，如果之后有需要提速可以考虑吧加载放到子线程中，
        // 使用加锁或者通知队列来管理后续使用保证使用时一定事加载完成的情况
        Set<String> routerMap = ClassUtils.getClassNamesByPackageName(application, GeneratorConfig.PACKAGE_OF_GENERATE_FILE);

        // 包含路由Root的类全民前缀 com.qire.antsrouter.routes.AntsRouter_Root_
        String rootClassNamePrefix = GeneratorConfig.PACKAGE_OF_GENERATE_FILE + "." + GeneratorConfig.CLASS_ROOT_PREFIX;
        // 包含拦截器Interceptor的类全民前缀 com.qire.antsrouter.routes.AntsRouter_Interceptor_
        String interceptorsClassNamePrefix = GeneratorConfig.PACKAGE_OF_GENERATE_FILE + "." + GeneratorConfig.CLASS_INTERCEPTOR_PREFIX;

        for (String className : routerMap) {
            if (className.startsWith(rootClassNamePrefix)) {
                // root中注册的是分组信息 将分组信息加入仓库中
                ((IRouteRoot) Class.forName(className).getConstructor().newInstance()).loadInto(groupsIndex);
            } else if (className.startsWith(interceptorsClassNamePrefix)) {
                // 加载拦截器规则元数据
                ((IInterceptorLoader) Class.forName(className).getConstructor().newInstance()).loadInto(interceptorsIndex);
            }
        }
        for (Map.Entry<String, Class<? extends IRouteGroup>> stringClassEntry : groupsIndex.entrySet()) {
            Log.d(TAG, "Root映射表[ " + stringClassEntry.getKey() + " : " + stringClassEntry.getValue() + "]");
        }

    }

    /**
     * 初始化导航拦截器
     * 参见{@link com.qire.antsrouter.AntsRouter#init AntsRouter.init()}。
     * @param context app应用设备上下文
     */
    protected static void init(final Context context){
        AntsThreadBuilder.defaultExecutor().execute(() -> {
            if (AssertUtils.isEmpty(MappingCache.interceptorsIndex)) {
                return;
            }
            for (Map.Entry<Integer, RulesMeta> rulesMetaEntry : MappingCache.interceptorsIndex.entrySet()) {
                RulesMeta rulesMeta = rulesMetaEntry.getValue();
                try {
                    InterceptRules rules = rulesMeta.<InterceptRules>getRuleClass().getConstructor().newInstance();
                    rules.init(context);
                    String[] groups = rulesMeta.getGroup();
                    for(String group : groups) {
                        List<InterceptRules> list = interceptorGroup.get(group);
                        if (AssertUtils.isEmpty(list)) {
                            list = new ArrayList<>();
                            interceptorGroup.put(group, list);
                        }
                        list.add(rules);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 查找routeMeta通过路径及组别
     * @param path  路径
     * @param group 组别
     * @return routeMeta 路由元数据
     */
    public static RouteMeta findRouteMetaBy(String path, String group){
        Class<? extends IRouteGroup> groupMeta = groupsIndex.get(group);

        if (null != groupMeta) {
            IRouteGroup iGroupInstance;
            try {
                iGroupInstance = groupMeta.getConstructor().newInstance();
            } catch (Exception e) {
                throw new RuntimeException("路由分组映射表记录失败.", e);
            }
            iGroupInstance.loadInto(routes);
            // 已经准备过了就可以移除了 (不会一直存在内存中)
            groupsIndex.remove(group);
        }
        RouteMeta routeMeta = routes.get(path);
        if(null == routeMeta) {
            throw new NoRouteFoundException("请检查组别与路径是否与注解描述匹配，没找到对应路由：分组=" + group + "  路径=" + path);
        }
        return routes.get(path);
    }

    /**
     * 查找拦截规则列表通过组别
     * @param group 组别字串
     * @return 返回包含当前组别使用的多有拦截规则，其中包含全局规则
     */
    public static List<InterceptRules> findRulesBy(String group){
        List<InterceptRules> interceptorList = new ArrayList<>();
        List<InterceptRules> allList = MappingCache.interceptorGroup.get("");
        List<InterceptRules> groupList = MappingCache.interceptorGroup.get(group);
        if(AssertUtils.notEmpty(allList)) {
            interceptorList.addAll(allList);
        }
        if(AssertUtils.notEmpty(groupList)) {
            interceptorList.addAll(groupList);
        }
        return interceptorList;
    }

}
